package com.three.iot.broker.controller;

import cn.hutool.core.util.HexUtil;
import cn.hutool.core.util.RandomUtil;
import com.three.iot.broker.vo.IotDeviceAlarmVo;
import com.three.iot.broker.vo.IotDevicePrintVo;
import com.three.iot.broker.vo.ItemVo;
import com.three.iot.commons.utils.DateUtil;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.paho.client.mqttv3.*;
import org.eclipse.paho.client.mqttv3.persist.MemoryPersistence;
import org.springframework.web.bind.annotation.*;

import javax.annotation.PostConstruct;
import java.nio.charset.Charset;

@RestController
@RequestMapping("/iot")
@Slf4j
public class MessageController {
    final MqttConnectOptions connOpts = new MqttConnectOptions();

    public  MqttClient initClient(String topic) {
        final String broker = "tcp://127.0.0.1:8885";
        final String clientId = "GID_XXX@@@ClientID_124";
        MemoryPersistence persistence = new MemoryPersistence();
        try {
            MqttClient sampleClient = new MqttClient(broker, clientId, persistence);
            log.info("Connecting to broker: {}", broker);
            connOpts.setServerURIs(new String[]{broker});
            connOpts.setUserName("testOne");
            connOpts.setPassword("testOne".toCharArray());
            connOpts.setCleanSession(true);
            connOpts.setKeepAliveInterval(90);
            connOpts.setAutomaticReconnect(true);
            connOpts.setMqttVersion(MqttConnectOptions.MQTT_VERSION_3_1);
            sampleClient.setCallback(new MqttCallbackExtended() {
                public void connectComplete(boolean reconnect, String serverURI) {
                    log.info("connect success");
                    //连接成功，需要上传客户端所有的订阅关系
                    try {
                        sampleClient.subscribe(topic, 0);
                    } catch (Exception ex) {
                        log.error("订阅发生异常:{}", ex);
                    }
                }
                public void connectionLost(Throwable throwable) {
                    log.error("server connection lost.", throwable);
                }

                public void messageArrived(String topic, MqttMessage mqttMessage) throws Exception {
                    log.info("message arrived. topic={}, message={}.", topic, new String(mqttMessage.getPayload()));
                }
                public void deliveryComplete(IMqttDeliveryToken iMqttDeliveryToken) {
                    log.info("delivery complete. messageId={}.", iMqttDeliveryToken.getMessageId());
                }
            });
            return sampleClient;
        } catch (MqttException e) {
            log.error("初始化客户端发生异常:{}",e);
        }
        return null;
    }

    /**
     * 打印机语音提示接口
     * @param iotDeviceAlarmVo
     * @return
     */
    @PostMapping("/alarm")
    public String alarm(@RequestBody IotDeviceAlarmVo iotDeviceAlarmVo){
            final String topic = iotDeviceAlarmVo.getDeviceNo();
        String content = iotDeviceAlarmVo.getDeviceNo()+"|"+
                iotDeviceAlarmVo.getReqType()+"|"+
                DateUtil.getDateStamp()+ RandomUtil.randomNumbers(5) +"|"+
                iotDeviceAlarmVo.getOrderAmt()+"|"+
                iotDeviceAlarmVo.getPayType()+"\r\n";
          return   sendMsg(topic,content.getBytes());
    }

    /**
     * 打印机打印小票接口
     * @param iotDevicePrintVo
     * @return
     */
    @PostMapping("/print")
    public String print(@RequestBody IotDevicePrintVo iotDevicePrintVo){
        final String topic = iotDevicePrintVo.getDeviceNo();
        byte[] content = buildPrintContent(iotDevicePrintVo);
        return   sendMsg(topic,content);
    }
    @PostMapping("/printTest")
    public String printTest(@RequestBody IotDevicePrintVo iotDevicePrintVo){
        final String topic = iotDevicePrintVo.getDeviceNo();
        byte[] content = buildPrintContent1(iotDevicePrintVo);
        return   sendMsg(topic,content);
    }

    @PostMapping("/print/{topicNo}/{contentStr}")
    public String printByte(@PathVariable("topicNo") String topicNo,@PathVariable("contentStr") String contentStr){
        return   sendMsg(topicNo,ByteUtils.hexToByteArray(contentStr));
    }

    /**
     * 1E 10: 固定头
     * multi_cnt: 重复打印次数
     * len_MSB: 有效数据长度, 高位字节, (有效数据长度包含打印内容和标识码和 CRC16 校验字节)
     * len_LSB: 有效数据长度, 低位字节, (有效数据长度包含打印内容和标识码和 CRC16 校验字节)
     * N1 N2 N3 ... Nk 打印内容
     * k 打印内容长度, k = (len_MSB*256 + len_LSB - 4) (k 表示 N1 N2 N3 ... Nk 的长度)
     * 1B 63: 标识字节
     * CRC16_MSB: CRC16 校验码, 高位字节, 对打印内容进行的 CRC16 校验, 仅对 N1 N2 N3 ... Nk 进行校验
     * CRC16_LSB: CRC16 校验码, 低位字节, 对打印内容进行的 CRC16 校验, 仅对 N1 N2 N3 ... Nk 进行校验
     * @param iotDevicePrintVo
     * @return
     */
    /*
     * 参数模型 NAME : CRC-16/X25 CRC=X16+X12+X5+1
     * 宽度 WIDTH : 16
     * 多项式 POLY(Hex) : 1021
     * 初始值 INIT(Hex) : FFFF
     * 结果异或值 XOROUT(Hex) : FFFF
     * 输入数据反转(REFIN) : 使能
     * 输出数据反转(REFOUT) : 使能
     * MSB/LSB : 高位在左, 低位在右
     * 校验工具网址 : http://www.ip33.com/crc.html
     */
    private byte[] buildPrintContent(IotDevicePrintVo iotDevicePrintVo) {
        byte[] headInt= {(byte) 0x1E, (byte) 0x10,(byte)0x01};
        log.info("headInt:{}",HexUtil.encodeHex(headInt,false));
        StringBuffer stb=new StringBuffer();
        //append(iotDevicePrintVo.getHeadName()).append("\r\n")
           stb.append(iotDevicePrintVo.getShopName()).append("\r\n")
           .append("订单号:").append(iotDevicePrintVo.getOrderNo()).append("\r\n")
           .append("支付时间:").append(iotDevicePrintVo.getPayTime()).append("\r\n");
        stb.append("-------------------------------\r\n");
        for (ItemVo itemVo:iotDevicePrintVo.getItemList()) {
            stb.append(itemVo.getItemName()).append("x").append(itemVo.getItemNum())
                    .append("    ").append(itemVo.getItemAmt()).append("元\r\n");
        }
        stb.append("-------------------------------\r\n");
        stb.append("原价:").append(iotDevicePrintVo.getOrderAmt()).append("元\r\n");
        stb.append("实际付款:").append(iotDevicePrintVo.getPayAmt()).append("元\r\n");
        stb.append("-------------------------------\r\n");
        stb.append(iotDevicePrintVo.getRecvAddress()).append(" ")
                .append(iotDevicePrintVo.getPhoneNum()).append(" ")
                .append(iotDevicePrintVo.getNickName()).append("\r\n");
        stb.append("-------------------------------\r\n");
        stb.append("用户备注:").append(iotDevicePrintVo.getOrderMemo()).append("\r\n");
        stb.append("\r\n----------分割线------------\r\n");
        String msgStr=stb.toString();
        byte[] lenByte=ByteUtils.toBytes(msgStr.length());
        int crcNum=CRC16Util.CRC16_X25(msgStr.getBytes());
        byte[] msgByte=msgStr.getBytes();
        byte[] flagByte={(byte)0x1B,(byte)0x63};
        byte[] crcByte=ByteUtils.toBytes(crcNum);
        byte[] resultByte = ByteUtils.byteMerger(headInt,lenByte,msgByte,flagByte,crcByte);
        return resultByte;
    }


    private byte[] buildPrintContent1(IotDevicePrintVo iotDevicePrintVo) {
        byte[] headInt= {(byte) 0x1E, (byte) 0x10,(byte)0x01};
        StringBuffer stb=new StringBuffer();
        stb.append("ABCD\r\n");
        String msgStr=stb.toString();
        byte[] lenByte=ByteUtils.toBytes(msgStr.length());
        byte[] msgByte=msgStr.getBytes();
        int crcNum=CRC16Util.CRC16_X25(msgStr.getBytes());
        byte[] flagByte={(byte)0x1B,(byte)0x63};
        byte[] crcByte=ByteUtils.toBytes(crcNum);
        byte[] resultByte = ByteUtils.byteMerger(headInt,lenByte,msgByte,flagByte,crcByte);
        return resultByte;
    }


    /**
     * 消息推送
     * @param topic 主题名称
     * @param content 消息内容
     * @return
     */
    private String sendMsg(String topic, byte[] content) {
        MqttClient sampleClient =initClient(topic);
        try {
            sampleClient.connect(connOpts);
            //此处消息体只需要传入 byte 数组即可，对于其他类型的消息，请自行完成二进制数据的转换
            final MqttMessage message = new MqttMessage(content);
            message.setQos(2);
            /* *
             * 消息发送到某个主题 Topic，所有订阅这个 Topic 的设备都能收到这个消息。
             * 遵循 MQTT 的发布订阅规范，Topic 也可以是多级 Topic。此处设置了发送到二级 Topic
             * */
            sampleClient.publish(topic, message);
            log.info("topic:【{}】 public message:【{}】",topic, ByteUtils.bytesToHex(content));
            sampleClient.disconnect();
            return "OK";
        } catch (Exception ex) {
            log.error("发送消息异常!{}",ex);
            return "ERROR";
        }
    }
}
